/*
     Copyright 2007 Joseph M. Ferner

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

using System;
using System.IO;
using System.Net;
using log4net;
using OpenServers.Net.Common.Utils;

namespace OpenServers.Net.Common.FileSystem.Http {
    /// <summary>
    /// A file which is backed by a HTTP url.
    /// </summary>
    public class HttpFile : IFile {
        private static readonly ILog log = LogManager.GetLogger(typeof(HttpFile));

        private readonly IDirectory _parent;
        private readonly Url _url;
        private FileInfo _cacheFile;
        private string _name;

        /// <summary>
        /// constructor.
        /// </summary>
        public HttpFile(IDirectory parent, Url url) {
            _parent = parent;
            _url = url;
        }

        /// <summary>
        /// open an input stream.
        /// </summary>
        public Stream OpenInputStream() {
            EnsureFileLoaded();
            if (CacheFile.Exists) {
                return new FileStream(CacheFile.FullName, FileMode.Open);
            }
            return null;
        }

        /// <summary>
        /// Not Implemented
        /// </summary>
        public Stream OpenOutputStream() {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Gets the size of a file object.
        /// </summary>
        public long Size {
            get {
                EnsureFileLoaded();
                return CacheFile.Length;
            }
        }

        /// <summary>
        /// The name of the object.
        /// </summary>
        public string Name {
            get {
                if (_name == null) {
                    string path = Url.Path;
                    int lastPathToken = path.LastIndexOf('/');
                    _name = path.Substring(lastPathToken + 1);
                }
                return _name;
            }
        }

        /// <summary>
        /// The last modified date.
        /// </summary>
        public DateTime LastModified {
            get {
                EnsureFileLoaded();
                return CacheFile.LastWriteTime;
            }
        }

        /// <summary>
        /// Not Implemented
        /// </summary>
        public void Delete() {
            throw new NotImplementedException();
        }

        /// <summary>
        /// flag whether the object exists.
        /// </summary>
        public bool Exists {
            get { return CacheFile.Exists; }
        }

        /// <summary>
        /// The parent object.
        /// </summary>
        public IDirectory Parent {
            get { return _parent; }
        }

        /// <summary>
        /// flag marking this object as read only.
        /// </summary>
        public bool ReadOnly {
            get { return true; }
        }

        /// <summary>
        /// Not Implemented
        /// </summary>
        public void MoveTo(IFileSystemObject fso) {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Gets the full path of this object.
        /// </summary>
        public string FullPath {
            get { return FileSystemUtils.GetFullPath(this); }
        }

        /// <summary>
        /// The url to the source of this file.
        /// </summary>
        public Url Url {
            get { return _url; }
        }

        /// <summary>
        /// The underlying cached file.
        /// </summary>
        public FileInfo CacheFile {
            get {
                if (_cacheFile == null) {
                    string cacheFileName;
                    if (Parent is HttpDirectory) {
                        cacheFileName = Path.Combine(((HttpDirectory)Parent).CacheDir.FullName, Name);
                    } else {
                        cacheFileName = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
                    }
                    _cacheFile = new FileInfo(cacheFileName);
                    if (_cacheFile.Exists) {
                        _cacheFile.Delete();
                    }
                }
                _cacheFile.Refresh();
                return _cacheFile;
            }
        }

        /// <summary>
        /// Ensure a file is loaded.
        /// </summary>
        protected virtual void EnsureFileLoaded() {
            if (!_cacheFile.Exists) {
                HttpWebRequest request = CreateRequest();
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                ProcessResponse(response);
            }
        }

        /// <summary>
        /// Process the response object.
        /// </summary>
        protected virtual void ProcessResponse(HttpWebResponse response) {
            Stream responseDataStream = response.GetResponseStream();
            Stream cacheFileStream = null;
            try {
                cacheFileStream = _cacheFile.OpenWrite();
                StreamUtils.Copy(responseDataStream, cacheFileStream);
                cacheFileStream.Close();
            } catch (Exception ex) {
                log.Error("getting file", ex);
                if (cacheFileStream != null) {
                    cacheFileStream.Close();
                }
                if (_cacheFile.Exists) {
                    _cacheFile.Delete();
                }
                throw;
            } finally {
                if (responseDataStream != null) {
                    responseDataStream.Close();
                }
            }

            if (response.LastModified != default(DateTime)) {
                _cacheFile.LastWriteTime = response.LastModified;
            }
        }

        /// <summary>
        /// Creates the request object.
        /// </summary>
        protected virtual HttpWebRequest CreateRequest() {
            return (HttpWebRequest)WebRequest.Create(Url.ToString());
        }
    }
}
